﻿using System;
using System.Collections.Generic;
using System.Text;
using Kyoh.App.Crawler.Plugin;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
using System.Xml.Linq;
using System.Runtime.InteropServices;
using Kyoh.Lib.Component.Globalization;
using Kyoh.Lib.Component;
using Kyoh.Lib.Plugin;
using System.Web;
using System.Diagnostics;

namespace Crawler.Parser.Smile
{
    [Guid("22989ECA-2578-487C-9172-6FD1EEAA0655")]
    public class SmileParser : MarshalByRefObject, IParser
    {
        private CookieContainer _cake = null;
        private static readonly Regex SupportedUriRegex = new Regex(@"http://www.nicovideo.jp/watch/(?<id>[a-z]{2}\d+)", RegexOptions.Compiled);
        public bool Supports(string uri)
        {
            return SupportedUriRegex.IsMatch(uri);
        }

        public ILoader Parse(string uri)
        {
            return Reparse(uri);
        }

        public ILoader Reparse(string uri)
        {
            var supported = SupportedUriRegex.Match(uri);
            if (!supported.Success)
                return null;

            // ログイン
            Login(false);

            for (int i = 0; i < 2; i++)
            {
                try
                {
                    var getinfo = @"http://ext.nicovideo.jp/api/getthumbinfo/" + supported.Groups["id"].Value;
                    var xml = XElement.Parse(GetHttpString(getinfo));
                    var thumb = xml.Element("thumb");
                    var video_id = thumb.Element("video_id");
                    var title = thumb.Element("title");
                    var summary = thumb.Element("description");
                    var thumbnail = thumb.Element("thumbnail_url");
                    var movie_type = thumb.Element("movie_type");
                    var base_uri = thumb.Element("watch_url");

                    var getflv = @"http://flapi.nicovideo.jp/api/getflv/" + supported.Groups["id"].Value;
                    var flv = GetHttpString(getflv);
                    var query = HttpUtility.ParseQueryString(flv);

                    string closed = string.Empty, url = string.Empty;
                    foreach (string name in query.Keys)
                    {
                        if (name == "closed")
                            closed = query[name];
                        else if (name == "url")
                            url = query[name];
                    }
                    if (closed != "1" && !string.IsNullOrWhiteSpace(url))
                        return new DefaultLoader()
                        {
                            ContentUri = query["url"],
                            FileName = title.Value + "." + movie_type.Value,
                            Summary = summary.Value,
                            Thumbnail = TypicalParser.GetThumbnail(thumbnail.Value, base_uri.Value),
                            Title = title.Value,
                            Uri = base_uri.Value,
                        };
                }
                catch (HttpException exc)
                {
                    if (i != 0)
                        throw;
                    else if (exc.WebEventCode == 403)
                    {
                        _cake = null;
                        Login(true);
                    }
                    else
                        throw;
                }
            }
            throw new InvalidOperationException();
        }

        private void Login(bool force)
        {
            if (!force && _cake != null)
                return;
            var mail = HttpUtility.UrlEncode(_preferences.Mail);
            var pass = HttpUtility.UrlEncode(_preferences.Pass);

            // ログイン情報の構築
            var param = Encoding.UTF8.GetBytes("mail=" + mail + "&" + "password=" + pass);

            // ログインページ
            var page = @"https://secure.nicovideo.jp/secure/login?site=niconico";

            _cake = new CookieContainer();

            // ログインページのURLからリクエストオブジェクトを生成
            var req = (HttpWebRequest)HttpWebRequest.Create(page);
            req.Method = "POST";
            req.ContentType = "application/x-www-form-urlencoded";
            req.ContentLength = param.Length;
            req.CookieContainer = _cake;

            // ログイン情報を書き込み
            using (var stream = req.GetRequestStream())
                stream.Write(param, 0, param.Length);

            // 送信
            string result;
            using (var response = req.GetResponse())
            using(var stream = response.GetResponseStream())
            using(var reader = new StreamReader(stream, Encoding.UTF8))
            {
                result = reader.ReadToEnd();
            }
        }

        private string GetHttpString(string uri)
        {
            var request = (HttpWebRequest)WebRequest.Create(uri);
            request.CookieContainer = _cake;
            using (var response = request.GetResponse())
            using (var reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                return reader.ReadToEnd();
        }

        public string Author
        {
            get { return "kyoh"; }
        }

        public Uri Uri
        {
            get { return new Uri(@"http://kyoh.net/"); }
        }

        public string Description
        {
            get { return "ニコニコ動画の動画を取得します。"; }
        }

        public string Name
        {
            get { return "SmileParser"; }
        }

        private SmileParserPreference _preferences;

        public object Preferences
        {
            get { return _preferences; }
            set
            {
                if (value is SmileParserPreference)
                    _preferences = (SmileParserPreference)value;
            }
        }

        public void Dispose()
        {
        }
    }
    [Serializable]
    public struct SmileParserPreference
    {
        [PropertyIndex(0)]
        [DisplayNameFromResource(typeof(Properties.SmileParserResources), "Mail")]
        public string Mail { get; set; }
        [PropertyIndex(1)]
        [DisplayNameFromResource(typeof(Properties.SmileParserResources), "Pass")]
        public string Pass { get; set; }
    }
}
